package com.atguigu.gmall.cart.service.impl;


import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.alibaba.nacos.common.utils.StringUtils;
import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.cart.service.CartBizService;
import com.atguigu.gmall.cart.vo.AddCartSuccessVo;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.common.util.UserAuthUtils;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.user.vo.UserAuthInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author lfy
 * @Description
 * @create 2022-10-17 10:22
 */
@Slf4j
@Service
public class CartBizServiceImpl implements CartBizService {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Override
    public AddCartSuccessVo addToCart(Long skuId, Integer skuNum) {
        //1、获取到当前用户信息。 工具类方法调用也是同一个线程
        //2、得到操作购物车用的key： cart:info:用户标识
        String cartKey = determinCartKey();

        // cart:info:3:    hash(49 - 商品信息)
        //3、给购物车中添加
        SkuInfo skuInfo = addCartItem(cartKey, skuId, skuNum);

        //4、返回前端需要的数据
        AddCartSuccessVo successVo = new AddCartSuccessVo();
        successVo.setSkuInfo(skuInfo);
        successVo.setSkuNum(skuNum);
        return successVo;

    }


    /**
     * 添加商品到购物车
     *
     * @param cartKey
     * @param skuId
     * @param skuNum
     * @return
     */
    public SkuInfo addCartItem(String cartKey, Long skuId, Integer skuNum) {
        SkuInfo result = null;
        //购物车中有无此商品。获取指定key的购物车
        BoundHashOperations<String, String, String> cart = getCart(cartKey);
        if (cart.hasKey(skuId.toString())) {
            //1、有：数量修改
            CartItem cartItem = getCartItem(cartKey, skuId);
            cartItem.setSkuNum(cartItem.getSkuNum() + skuNum);
            if(cartItem.getSkuNum() > RedisConst.CART_ITEM_LENGTH){
                throw new GmallException(ResultCodeEnum.CART_ITEM_OVERLIMIT);
            }
            //保存到redis
            //为了保证。购物车中保存的商品的价格不是以前的。
            Result<BigDecimal> skuPrice = skuDetailFeignClient.getSkuPrice(skuId);
            cartItem.setSkuPrice(skuPrice.getData());
            cart.put(skuId.toString(), Jsons.toStr(cartItem));
            //返回页面要用的结果
            result = convertCartItem2SkuInfo(cartItem);
        } else {
            if(cart.size() >= RedisConst.CART_SIZE){
                throw new GmallException(ResultCodeEnum.CART_SIZE_OVERLIMIT);
            }
            if(skuNum > RedisConst.CART_ITEM_LENGTH){
                throw new GmallException(ResultCodeEnum.CART_ITEM_OVERLIMIT);
            }
            //2、无：新增。
            Result<SkuInfo> skuInfo = skuDetailFeignClient.getSkuInfo(skuId);
            //3、保存到购物车
            CartItem cartItem = convertSkuInfo2CartItem(skuInfo.getData());
            cartItem.setSkuNum(skuNum);
            cart.put(skuId.toString(), Jsons.toStr(cartItem));
            result = skuInfo.getData();
        }

        return result;
    }

    private SkuInfo convertCartItem2SkuInfo(CartItem cartItem) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(cartItem.getId());
        skuInfo.setPrice(cartItem.getSkuPrice());
        skuInfo.setSkuName(cartItem.getSkuName());
        skuInfo.setSkuDefaultImg(cartItem.getSkuDefaultImg());
        return skuInfo;
    }

    /**
     * 从指定购物车中得到一个商品
     *
     * @param cartKey
     * @param skuId
     * @return
     */
    public CartItem getCartItem(String cartKey, Long skuId) {
        //1、从远程redis拿到这个商品
        String json = redisTemplate.opsForHash().get(cartKey, skuId.toString()).toString();
        if (!StringUtils.isEmpty(json)) {
            return Jsons.toObj(json, CartItem.class);
        }
        return null;
    }

    
    @Autowired
    ThreadPoolExecutor corePool;
    @Override
    public List<CartItem> getCartItemList(String cartKey) {
        //1、得到购物车
        BoundHashOperations<String, String, String> cart = getCart(cartKey);


        //2、拿到购物车中的所有值。key不用管； 数据需要按照操作商品加入购物车的时间有序排列。
        //最后一个加到购物车的在最上面显示。展示购物车列表的时候
        List<CartItem> cartItems = cart.values()
                .stream()
                .map(str -> {
                            CartItem cartItem = Jsons.toObj(str, CartItem.class);
                            //从redis拿东西快
//                            BigDecimal price = getCartItem1010Price(cartItem.getSkuId());
//                            cartItem.setSkuPrice(price);
                            return cartItem;
                        }
                ).sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                .collect(Collectors.toList());

        //拿到老请求。老线程
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        //启动一个异步任务
        CompletableFuture.runAsync(()->{
            //新线程共享
//          RequestContextHolder.setRequestAttributes(requestAttributes);
            updateCartItemsPrice(cartKey,cartItems);
            //移除
//          RequestContextHolder.resetRequestAttributes();
        },corePool);


        //直接返回。第一次看不到，后面能看到最新价格
        return cartItems;
    }

    private void updateCartItemsPrice(String cartKey, List<CartItem> cartItems) {
        log.info("正在后台帮用户同步最新价格...");

        cartItems.stream().forEach(cartItem -> {
            Long skuId = cartItem.getSkuId();
            //查到最新价格。 内部的feign拦截器发生了空指针异常。不关心用户信息
            Result<BigDecimal> skuPrice = skuDetailFeignClient.getSkuPrice(skuId);
            if(!cartItem.getSkuPrice().equals(skuPrice.getData())){
                //更新价格
                cartItem.setSkuPrice(skuPrice.getData());
                log.info("skuId:{} 最新价格： {} ",cartItem.getSkuId(),skuPrice.getData());
                //保存到redis
                saveCartItem(cartItem,cartKey);
            }
        });
    }

    @Override
    public void updateCartItemNum(Long skuId, Integer num) {
        //1、得到需要使用的购物车的键
        String cartKey = determinCartKey();

        //2、从购物车中得到商品
        CartItem cartItem = getCartItem(cartKey, skuId);
        cartItem.setSkuNum(cartItem.getSkuNum() + num);

        //3、保存到购物车
        saveCartItem(cartItem, cartKey);

    }

    @Override
    public void saveCartItem(CartItem item, String cartKey) {
        BoundHashOperations<String, String, String> cart = getCart(cartKey);
        cart.put(item.getSkuId().toString(), Jsons.toStr(item));
    }

    @Override
    public void checkCartItem(Long skuId, Integer checkStatus) {
        //1、得到购物车的键
        String cartKey = determinCartKey();
        //2、得到商品，并修改信息
        CartItem item = getCartItem(cartKey, skuId);
        item.setIsChecked(checkStatus);
        //3、同步到redis
        saveCartItem(item, cartKey);
    }

    @Override
    public void deleteCartItem(Long skuId) {
        String cartKey = determinCartKey();
        BoundHashOperations<String, String, String> cart = getCart(cartKey);

        cart.delete(skuId.toString());
    }

    @Override
    public void deleteChecked() {
        //1、找到这个购物车中哪些商品是选中的
        String cartKey = determinCartKey();
        //2、拿到所有选中商品
        List<CartItem> items = getCheckedCartItems(cartKey);


        //4、删除
        if (items.size() > 0) {
            //3、拿到所有id集合
            String[] ids = new String[items.size()];
            for (int i = 0; i < items.size(); i++) {
                ids[i] = items.get(i).getSkuId().toString();
            }
            BoundHashOperations<String, String, String> cart =
                    getCart(cartKey);

            cart.delete(ids);
        }

    }

    @Override
    public List<CartItem> getCheckedCartItems(String cartKey) {
        List<CartItem> itemList = getCartItemList(cartKey);

        //选中的商品
        List<CartItem> checkedList = itemList.stream()
                .filter(item -> item.getIsChecked() == 1)
                .collect(Collectors.toList());
        return checkedList;
    }

    @Override
    public List<CartItem> mergeCart() {
        //1、得到用户信息
        UserAuthInfoVo authInfo = UserAuthUtils.getUserAuthInfo();
        Long userId = authInfo.getUserId();
        String userTempId = authInfo.getUserTempId();


        //临时购物车没东西（1、没操作过   2、发生过一次合并）
        String tempCartKey = RedisConst.CART_INFO + userTempId;
        //2、得到临时购物车所有数据
        List<CartItem> tempItems = getCartItemList(tempCartKey);


        String userCartKey = RedisConst.CART_INFO + userId;
        //3、用户登录了，并且临时购物车有东西。就需要合并
        if (userId != null && tempItems != null && tempItems.size() > 0) {
            //1、如果 用户购物车有这个商品，就是数量修改，如果没有就是重新保存到购物车
            //临时购物车所有数据添加到用户购物车

            //2、拿到临时购物车中每一个商品，放到用户购物车
            for (CartItem tempItem : tempItems) {
                //有则修改，无则新增
                addCartItem(userCartKey, tempItem.getSkuId(), tempItem.getSkuNum());
            }

            //3、清空临时购物车
            clearCart(tempCartKey);
        }
        //以用户购物车的最终数据为准
        List<CartItem> itemList = getCartItemList(userCartKey);
        return itemList;
    }

    @Override
    public void clearCart(String cartKey) {
        redisTemplate.delete(cartKey);
    }

    @Override
    public BigDecimal getCartItem1010Price(Long skuId) {
        String price = redisTemplate.opsForValue().get(RedisConst.SKU_PRICE + skuId);

        return new BigDecimal(price);
    }

    @Override
    public void expireTempCart(String cartKey) {
        // -1：永久
        Long expire = redisTemplate.getExpire(cartKey);
        if(expire < 0){
            //给key给过过期时间
            redisTemplate.expire(cartKey,RedisConst.CART_TEMP_TTL, TimeUnit.DAYS);
        }
    }

    private CartItem convertSkuInfo2CartItem(SkuInfo data) {
        CartItem cartItem = new CartItem();
        cartItem.setId(data.getId());
        cartItem.setSkuId(data.getId());
        cartItem.setCartPrice(data.getPrice()); //购物车价格
        cartItem.setSkuPrice(data.getPrice()); //实时价格
        cartItem.setSkuDefaultImg(data.getSkuDefaultImg());
        cartItem.setSkuName(data.getSkuName());
        cartItem.setIsChecked(1); //1代表选中。0不选中
        cartItem.setCreateTime(new Date());
        cartItem.setUpdateTime(new Date());

        return cartItem;
    }


    /**
     * 获取当前购物车
     *
     * @param cartKey
     * @return
     */
    private BoundHashOperations<String, String, String> getCart(String cartKey) {
        BoundHashOperations<String, String, String> cart = redisTemplate
                .boundHashOps(cartKey);
        return cart;
    }

    /**
     * 决定购物车用哪个key操作
     *
     * @return
     */
    public String determinCartKey() {
        UserAuthInfoVo info = UserAuthUtils.getUserAuthInfo();
        Long userId = info.getUserId();
        String userTempId = info.getUserTempId();
        String cartkKey = RedisConst.CART_INFO;

        //有用户id优先使用 用户id作为购物车key
        if (userId == null) {
            cartkKey = cartkKey + userTempId;
        } else {
            cartkKey = cartkKey + userId;
        }
        return cartkKey;
    }
}
